package com.googlecode.dgwt.client.dijit.view.render.base;

import java.util.Collections;
import java.util.List;

import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.user.client.DOM;
import com.googlecode.dgwt.client.dijit.Dijit;
import com.googlecode.dgwt.client.dijit.View;
import com.googlecode.dgwt.client.dojo.Dojo;
import com.googlecode.dgwt.client.dojo.core.JsMap;
import com.googlecode.dgwt.client.dojo.util.NativeList;

/**
 * @author wellsantos@wellsantos.com
 * @since 0.01
 */
public abstract class RowRenderBase extends ViewRenderBase implements RowRender {

    private static final String ROW_METAINF_PROPERTY = "__dijitRowRender_rowMetaInf";
    private static final String ROW_FLAG_PROPERTY = "__dijitRowRender_rowFlag";
    
    
    private NativeList<Element> rowList;
    
    private List<Element> rowListReadOnly;
    
    
    public RowRenderBase(View view) {
        super(view);
        rowList = new NativeList<Element>();
    }

    
    protected abstract Element createRowElement();
    
    protected abstract void internalUpdateRow(int rowIndex, Object metaInf);

    protected NativeList<Element> getRowList() {
        return rowList;
    }
    
    public void addRow(int rowIndex) {
        addRow(rowIndex, null);
    }
    
    @Override
    public void addRow(int rowIndex, Object metaInf) {
        internalAddRow(rowIndex);
        updateRow(rowIndex, metaInf);
    }

    protected void attachRow(Element rowElement, int rowIndex){
        DOM.insertChild(
                getView().getWidget().getContainerElement().<com.google.gwt.user.client.Element>cast(), 
                rowElement.<com.google.gwt.user.client.Element>cast(), rowIndex);
    }
    
    protected void detachRow(Element rowElement, int rowIndex){
        Node rowParent = rowElement.getParentNode();
        if (rowParent != null){
            rowParent.removeChild(rowElement);
            Dojo.destroy(rowElement);
        }
    }
    
    protected void internalAddRow(int rowIndex) {
        Element rowElement = createRowElement();
        rowElement.setPropertyBoolean(ROW_FLAG_PROPERTY, true);
        Dojo.addClass(rowElement, Dijit.getTheme().dijitRowName());
        getRowList().add(rowElement);
        attachRow(rowElement, rowIndex);
    }

    public void removeRow(int rowIndex) {
        Element rowElement = getRow(rowIndex);
        getRowList().remove(rowIndex);
        detachRow(rowElement, rowIndex);
    }
    
    @Override
    public void addRows(int rowIndex, int rowCount) {
        for (int i=0; i<rowCount; i++){
            addRow(rowIndex+i);
        }
    }

    @Override
    public void removeRows(int rowIndex, int rowCount) {
        for (int i=rowCount-1; i>=0; i--){
            removeRow(rowIndex+i);
        }
    }

    @Override
    public void setRowCount(int rowCount) {
        assert (rowCount >= 0);
        int size = getRowCount();
        addRows(size, rowCount-size);
        removeRows(rowCount, size-rowCount);
    }

    @Override
    public void updateRows(int rowIndex, int rowCount) {
        for (int i=0; i<rowCount; i++){
            updateRow(rowIndex+i);
        }
    }

    @Override
    public void updateRow(int rowIndex) {
        updateRow(rowIndex, null);
    }

    @Override
    public void updateRow(int rowIndex, Object metaInf) {
        if (metaInf != null){
            getRow(rowIndex).<JsMap<Object>>cast().set(ROW_METAINF_PROPERTY, metaInf);
        } else {
            metaInf = getRow(rowIndex).<JsMap<Object>>cast().get(ROW_METAINF_PROPERTY);
        }
        internalUpdateRow(rowIndex, metaInf);
    }
    
    @Override
    public void updateRows() {
        updateRows(0, getRowCount());
    }


    @Override
    public void updateRows(Object metaInf) {
        if (metaInf == null){
            updateRows(0, getRowCount());
            return;
        }
        for (int rowIndex=0; rowIndex<getRowCount(); rowIndex++){
            if (metaInf.equals(getMetaInf(rowIndex))){
                internalUpdateRow(rowIndex, metaInf);
            }
        }
    }

    protected Object getMetaInf(int rowIndex){
        return getRow(rowIndex).<JsMap<Object>>cast().get(ROW_METAINF_PROPERTY);
    }

    @Override
    public Element getRow(int rowIndex) {
        return getRowList().get(rowIndex);
    }

    @Override
    public int getRowIndex(Element element) {
        Node node = element;
        while (node != null){
            if (node.<Element>cast().getPropertyBoolean(ROW_FLAG_PROPERTY)){
                return getRowList().indexOf(node);
            }
            node = node.getParentNode();
        }
        return -1;
    }

    @Override
    public List<Element> getRows() {
        if (rowListReadOnly == null){
            rowListReadOnly = Collections.unmodifiableList(rowList);
        }
        return rowListReadOnly;
    }

    @Override
    public int getRowCount() {
        return getRowList().size();
    }

}
